/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */
package info.peigen.hotpot.component.gateway.core.http.ssl;

import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.component.gateway.core.http.OkHttpClientUtil;
import info.peigen.hotpot.component.gateway.core.http.request.HttpRequest;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;

import javax.net.ssl.*;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * <b>(SslSocketHttpClientBase)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2019-08-11
 */
@Slf4j
public class SslSocketHttpClientBase {

    private final static String HTTPS_HEAD = "https";

    /**
     * 带信任证书，支持TLSv1.1,TLSv1.2
     *
     * @param client OkHttpClient
     *
     * @return OkHttpClient.Builder
     */
    public OkHttpClient.Builder compactTls(OkHttpClient.Builder client) {
        try {
            X509TrustManager trustManager     = systemDefaultTrustManager();
            SSLSocketFactory sslSocketFactory = sslSocketFactory(trustManager);
            client.sslSocketFactory(sslSocketFactory, trustManager);
        } catch (Exception exc) {
            log.error("Error while setting TLS compact", exc);
            compactNoTls(client);
        }
        return client;
    }

    /**
     * 不带信任证书，支持所有的TLS
     *
     * @param client OkHttpClient
     *
     * @return OkHttpClient.Builder
     */
    private OkHttpClient.Builder compactNoTls(OkHttpClient.Builder client) {
        try {
            X509TrustManager trustManager     = noValidateTrustManager();
            SSLSocketFactory sslSocketFactory = sslSocketFactory(trustManager);
            client.sslSocketFactory(sslSocketFactory, trustManager);
        } catch (Exception exc) {
            log.error("Error while setting TLS compact", exc);
        }
        return client;
    }

    private X509TrustManager systemDefaultTrustManager() {
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            return (X509TrustManager) trustManagers[0];
        } catch (GeneralSecurityException e) {
            // The system has no TLS. Just give up.
            throw new AssertionError();
        }
    }

    private X509TrustManager noValidateTrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        };
    }

    private SSLSocketFactory sslSocketFactory(X509TrustManager trustManager) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, null);
            return new TlsSocketFactory(sslContext.getSocketFactory());
        } catch (GeneralSecurityException e) {
            // The system has no TLS. Just give up.
            throw new AssertionError();
        }
    }

    protected OkHttpClient initClient(HttpRequest req) {
        if (StrUtil.contains(req.getRequestUrl(), HTTPS_HEAD)) {
            return compactTls(OkHttpClientUtil.getInstance().newBuilder())
                    .connectionSpecs(Arrays.asList(
                            ConnectionSpec.MODERN_TLS,
                            ConnectionSpec.COMPATIBLE_TLS
                    ))
                    .hostnameVerifier(new TrustAllHostnameVerifier())
                    .connectTimeout(req.getConnectTimeout(), TimeUnit.MILLISECONDS)
                    .readTimeout(req.getReadTimeout(), TimeUnit.MILLISECONDS)
                    .build();
        } else {
            return OkHttpClientUtil.getInstance().newBuilder()
                    .connectTimeout(req.getConnectTimeout(), TimeUnit.MILLISECONDS)
                    .readTimeout(req.getReadTimeout(), TimeUnit.MILLISECONDS)
                    .build();
        }
    }

}